package wy.hanyufang.cn.utils;

import com.google.zxing.*;
import com.google.zxing.client.j2se.BufferedImageLuminanceSource;
import com.google.zxing.common.BitMatrix;
import com.google.zxing.common.HybridBinarizer;
import com.google.zxing.qrcode.QRCodeReader;
import com.google.zxing.qrcode.decoder.ErrorCorrectionLevel;
import org.junit.jupiter.api.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.util.Assert;
import sun.jvm.hotspot.utilities.BitMap;

import javax.imageio.ImageIO;
import java.awt.*;
import java.awt.geom.RoundRectangle2D;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.nio.charset.StandardCharsets;
import java.util.*;

/**
 * 二维码工具类
 * @author ： wyhanyufang@163.com
 * @time ： 4:17 PM 2021/9/20
 */
public class QRCodeUtil {
    private static final Logger logger = LoggerFactory.getLogger(QRCodeUtil.class);
    private static int DEFAULT_WIDTH = 400;//默认宽度
    private static int DEFAULT_HEIGHT = 400;//默认高度
    private static int DEFAULT_BACK_COLOR = 0xffffff;//背景颜色默认为白色
    private static int DEFAULT_COLOR = 0x000000;//背景颜色默认为黑色

    static String qrCodePath = "/Users/apple/Documents/hanyufang/code-scanning-login/test.png";
    /***
     * 生成二维码
     * @param content  消息内容
     * @param savePath 文件图片保存路径
     * @return : void
     * @author ： wyhanyufang@163.com
     * @date ： 2021/9/20 10:25 PM
     */
    public static void generatorQRCode(String content,String savePath){
        generatorQRCode(content,savePath,null,null);
    }

    public static void generatorQRCode(String content,OutputStream outputStream){
        generatorQRCode(content,null,null,outputStream);
    }

    /***
     * 生成带有log的二维码，并保存到指定文件
     * @param content 二维码信息
     * @param logPath log图片位置
     * @param savePath 二维码保存路径
     * @return : void
     * @author ： wyhanyufang@163.com
     * @date ： 2021/9/23 9:23 AM
     */
    public static void generatorLogQRCode(String content,String logPath,String savePath){
        generatorQRCode(content,savePath,logPath,null);
    }

    public static void generatorLogQRCode(String content,String logPath,OutputStream stream){
        generatorQRCode(content,null,logPath,stream);
    }

    /***
     * 公用的，用来创建二维码方法
     * @param content  创建的消息内容
     * @param savePath 如果不为空，则将生成的二维码保存到该路径下
     * @param logPath 如果不为空，则说明会在二维码中生成log
     * @param outputStream 如果不为空，则会将生成的二维码以流的方式返回
     * @return : void
     * @author ： wyhanyufang@163.com
     * @date ： 2021/9/23 8:37 AM
     */
    public static void generatorQRCode(String content, String savePath,String logPath, OutputStream outputStream){
        Assert.notNull(content,"生成的二维码数据不能为空！");

        //二维码相关样式设置
        Map<EncodeHintType,Object>  hints = new HashMap<>();
        hints.put(EncodeHintType.CHARACTER_SET, StandardCharsets.UTF_8);//设置字符编码类型
        //如果添加了log，则需要把容错设置高一点，否则会扫码失败
        hints.put(EncodeHintType.ERROR_CORRECTION, ErrorCorrectionLevel.H);//设置误差校正,设置二维码排错率，可选L(7%)、M(15%)、Q(25%)、H(30%)，排错率越高可存储的信息越少，但对二维码清晰度的要求越小
        hints.put(EncodeHintType.MARGIN, 1);//设置二维码边距，单位像素，值越小，二维码距离四周越近

        //生成二维码
        MultiFormatWriter writer = new MultiFormatWriter();

        //图片
        BufferedImage bufferedImage = new BufferedImage(DEFAULT_WIDTH, DEFAULT_HEIGHT, BufferedImage.TYPE_INT_RGB);

       try{
           //矩阵，也就是二维码
           BitMatrix bitMatrix = writer.encode(content, BarcodeFormat.QR_CODE, DEFAULT_WIDTH, DEFAULT_HEIGHT, hints);
           //数据填充
           for (int x = 0; x < DEFAULT_WIDTH; x++) {
               for (int y = 0; y < DEFAULT_HEIGHT; y++) {
                   //填充图片颜色，0xFFFFFF黑色，0x000000白色
                   bufferedImage.setRGB(x, y, bitMatrix.get(x, y) ? DEFAULT_BACK_COLOR : DEFAULT_COLOR);
               }
           }
       }
       catch (Exception ex){
           logger.error("填充二维码出现异常",ex);
       }

        //添加二维码log
        Optional.ofNullable(logPath)
                .ifPresent(r->{
                    try{
                        //二维码绘图对象
                        Graphics2D qrGraphic = bufferedImage.createGraphics();
                        int qrWidth = bufferedImage.getWidth();
                        int qrHeight = bufferedImage.getHeight();

                        //读取到log图像
                        BufferedImage logBuffered = ImageIO.read(new File(logPath));
                        //将log图片绘制进入二维码,定位log位置
                        qrGraphic.drawImage(logBuffered,qrWidth/2 - qrWidth/5/2,qrHeight/2 - qrHeight/5/2,
                                qrWidth/5,qrHeight/5,null);
                        //画笔对象
                        BasicStroke basicStroke = new BasicStroke(5,BasicStroke.CAP_ROUND,BasicStroke.JOIN_ROUND);
                        qrGraphic.setStroke(basicStroke);
                        //log设置圆角
                        RoundRectangle2D.Float roundRectangle2D =
                                new RoundRectangle2D.Float(qrWidth/2 - qrWidth/5/2,qrHeight/2 - qrHeight/5/2
                                        ,qrWidth/5,qrHeight/5,20,20);
                        qrGraphic.setColor(Color.WHITE);
                        qrGraphic.draw(roundRectangle2D);

                        //设置图片的灰色边框
                        BasicStroke bordStroke = new BasicStroke(1,BasicStroke.CAP_ROUND,BasicStroke.JOIN_ROUND);
                        qrGraphic.setStroke(bordStroke);
                        RoundRectangle2D.Float bordRound =
                                new RoundRectangle2D.Float(qrWidth/2 - qrWidth/5/2,qrHeight/2 - qrHeight/5/2,
                                        qrWidth/5,qrHeight/5,20,20);
                        qrGraphic.setColor(new Color(128,128,128));
                        qrGraphic.draw(bordRound);

                        qrGraphic.dispose();
                        bufferedImage.flush();
                    }
                    catch (Exception ex){
                        logger.error("二维码添加log发生异常",ex);
                    }
                });

        //以文件的形式保存二维码
        Optional.ofNullable(savePath)
                .ifPresent(r-> {
                    try {
                        File file = new File(savePath);
                        if (file.exists()){
                            file.delete();
                        }
                        ImageIO.write(bufferedImage,"png",file);
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                });

        //以流的方式返回
        Optional.ofNullable(outputStream)
                .ifPresent(r-> {
                    try {
                        ImageIO.write(bufferedImage,"png",outputStream);
                    } catch (IOException e) {
                        logger.error("二维码文件写入流出现异常",e);
                    }
                });
    }

    public static String decodeQRCode(Object file){
        try{
            //读取图片为内存缓冲图像
            BufferedImage bufferedImage = null;
            if (file instanceof File){
                bufferedImage = ImageIO.read((File) file);
            }
            else if (file instanceof InputStream){
                bufferedImage = ImageIO.read((InputStream) file);
            }
            else if (file instanceof String){
                bufferedImage = ImageIO.read(new File((String) file));
            }

            assert bufferedImage != null;
            LuminanceSource source = new BufferedImageLuminanceSource(bufferedImage);
            Binarizer binarizer = new HybridBinarizer(source);
            BinaryBitmap binaryBitmap = new BinaryBitmap(binarizer);
            Map<DecodeHintType, Object> hints = new HashMap<DecodeHintType, Object>();
            hints.put(DecodeHintType.CHARACTER_SET, "UTF-8");
            //精度
            hints.put(DecodeHintType.TRY_HARDER, Boolean.TRUE);
            //复杂模式
            hints.put(DecodeHintType.PURE_BARCODE, Boolean.TRUE);
            Result result = new MultiFormatReader().decode(binaryBitmap, hints);// 对图像进行解码

            return result.getText();
        }
        catch (Exception ex){
            logger.error("二维码解析异常",ex);
        }
        return "";
    }

   @Test
    public void generationQRCodeTest(){
       generatorQRCode("hello world,少年自有少年强，放光芒",qrCodePath);
   }

   @Test
    public void decodeQRCodeTest(){
       System.out.println(decodeQRCode(new File(qrCodePath)));
   }

   @Test
    public void generatorLogQRCode(){
        generatorLogQRCode("https://www.baidu.com",
                "/Users/apple/Documents/hanyufang/code-scanning-login/touxiang.jpeg",
                "/Users/apple/Documents/hanyufang/code-scanning-login/touxiang.png");
   }
}
